package uk.ac.aber.paws.server.util;

import java.io.*;
import java.util.logging.Level;

import uk.ac.aber.paws.server.core.PAWSInstaller;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 21st May 2012
 * 
 * FileOperations.java
 * 
 * Performs file and directory operations
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class FileOperations {

	/**
	 * Mirrors 2 directory structures
	 * @param sourceDir The source directory to be mirrored
	 * @param destDir The destination directory to be store the replicated files
	 */
	public static void mirrorDirectories(File sourceDir, File destDir) {
	
		File[] list = sourceDir.listFiles();
		
		for (int i=0;i<list.length;i++) {
			
			File thisObj = list[i];
			
			if (thisObj.isDirectory()) {
				mirrorDirectories(thisObj,new File(destDir+"/"+thisObj.getName()));
			}
			else if (thisObj.isFile()) {
				
				File destFile = new File(destDir+"/"+thisObj.getName());
					
				if (destFile.exists()) {
					
					if (destFile.lastModified() != thisObj.lastModified() || destFile.length() != thisObj.length()) {
					
						try {
							copyFile(thisObj,destFile,true,true);
						} 
						catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				else {
					try {
						copyFile(thisObj,destFile,true,true);
					} 
					catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
	
	/**
	 * Deletes the specified directory
	 * @param path The path the directory to be deleted
	 */
	public static void deleteDirectory(String path) {
		
		if (new File(path).exists()) {
			File[] list = new File(path).listFiles();
			
			for (int i=0;i<list.length;i++) {
				
				File thisObj = list[i];
				
				if (thisObj.isDirectory()) {
					deleteDirectory(thisObj.getPath());
				}
				else if (thisObj.isFile()) {
					thisObj.delete();
				}
			}
		}
	}
	
	/**
	 * Copy the file
	 * @param source The file object of the file to be copied
	 * @param dest The file object for the destination file
	 * @throws IOException
	 */
	public static boolean copyFile(File source, File dest) throws IOException {
		
		BufferedInputStream in = null;
		BufferedOutputStream out = null;
		
		try {
			in = new BufferedInputStream(new FileInputStream(source));
			out = new BufferedOutputStream(new FileOutputStream(dest));
			
			copyStream(in, out);
			
			in.close();
			out.close();
		}
		finally {
			if (out != null) {
				out.close();
			}
			if (in != null) {
				in.close();
			}    
		}
		
		while (!dest.exists()){
			try {
				Thread.sleep(1000);
			}
			catch(Exception ex) {
				System.out.println(ex);
				return false;
			}
		}
		
		while (dest.length() != source.length()){
			try {
				Thread.sleep(1000);
			}
			catch(Exception ex) {
				System.out.println(ex);
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 Copies a file from source to destination, offering the option of preserving the last 
	 modified date between the 2 files and also an option to make the destination directories
	 of the destination file should they not exist already.
	 @param source The source File instantiation
	 @param dest The destination File instantiation
	 @param preserveTime True to preserve the timestamp on the dest file to be the same as the source file
	 @param mkdirs Whether to make the directories hosting the destination file. True to create the directories.
	 */
	public static void copyFile(File source, File dest, boolean preserveTime, boolean mkdirs) throws IOException {

		BufferedInputStream in = null;
		BufferedOutputStream out = null;
			
		try {
		
			if (mkdirs) {
				(new File(dest.getParent())).mkdirs();
			}
		
			long lastMod = source.lastModified();
			
			in = new BufferedInputStream(new FileInputStream(source));
			out = new BufferedOutputStream(new FileOutputStream(dest));
			
			copyStream(in, out);
			
			in.close();
			out.close();
			
			dest.setLastModified(lastMod);
		}
		finally {
			if (out != null) {
				out.close();
			}
			if (in != null) {
				in.close();
			}    
		}
		
		while (!dest.exists()){
			try {
				Thread.sleep(1000);
			}
			catch(Exception ex) {
				System.out.println(ex);
			}
		}
	}
	
	/**
	 Copies a file from source to destination, offering the option of preserving the last 
	 modified date between the 2 files.
	 @param source The source File instantiation
	 @param dest The destination File instantiation
	 @param preserveTime True to preserve the timestamp on the dest file to be the same as the source file
	 */
	public static void copyFile(File source, File dest, boolean preserveTime) throws IOException {
	
		BufferedInputStream in = null;
		BufferedOutputStream out = null;
		
		
		try {
			
			long lastMod = source.lastModified();
			
			in = new BufferedInputStream(new FileInputStream(source));
			out = new BufferedOutputStream(new FileOutputStream(dest));
			
			copyStream(in, out);
			
			in.close();
			out.close();
			
			dest.setLastModified(lastMod);
		}
		finally {
			if (out != null) {
				out.close();
			}
			if (in != null) {
				in.close();
			}    
		}
		
		while (!dest.exists()){
			try {
				Thread.sleep(1000);
			}
			catch(Exception ex) {
				System.out.println(ex);
			}
		}
	}
	
	/**
	 Performs the actual streaming between the two streams to perform the byte 2 byte
	 copy of the contents of each file.
	 @param in The input stream from which to read the byte stream
	 @param out The output stream in which to write the byte stream
	 */
	private static void copyStream(InputStream in, OutputStream out) throws IOException {
	
		byte[] buffer = new byte[1024];
		int numberRead;
		
		while ((numberRead = in.read(buffer)) >= 0) {
			out.write(buffer, 0, numberRead);
		}
	}
	
	/**
	 * Converts a path name with the unix directory markers to a windows formatted file path
	 * @param thePath The path to be altered
	 * @return The formatted file path 
	 */
	public static String convertUNIXtoDOSpath(String thePath) {
		
		char[] tmpChars = thePath.toCharArray();
		for (int i=0;i<tmpChars.length;i++) {
			if (tmpChars[i] == '/') {
				tmpChars[i] = '\\';
			}
		}
	
		return new String(tmpChars);
	}
}
